package valuecomusa.fijibus.eticket.db.shorthaul;

import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.util.Log;
import valuecomusa.fijibus.eticket.Utilities;
import valuecomusa.fijibus.eticket.dto.GpsCurrentLocationValue;

import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

public class GpsDBHandler extends SQLiteOpenHelper
{
	private static final Object synchronizedLock = "LOCK";
	// All Static variables
	// Database Version
	private static final int DATABASE_VERSION = 1;
    private final String TAG = "GpsDB-Short";
	// Database Name
	private static final String DATABASE_NAME = "shorthaul_gpsstore.db";

	public GpsDBHandler(Context context)
	{
		super(context, DATABASE_NAME, null, DATABASE_VERSION);
	}

	// Creating Tables
	@Override
	public void onCreate(SQLiteDatabase db)
	{
		Log.d(TAG, "Setup new database...");
		try
		{
			String CREATE_TABLE_GPS_LOCATION = "create table GpsCurrentLocation (\n" +
                    "\tlongitude real,\n" +
                    "\tlatitude real,\n" +
                    "\taltitude real,\n" +
                    "\tbearing real,\n" +
                    "\tspeed real,\n" +
                    "\taccuracy real,\n" +
                    "\tcloseststage int,\n" +
                    "\tclosestbusstation int,\n" +
                    "\tjournalisn int\n" +
                    ");";
            db.execSQL(CREATE_TABLE_GPS_LOCATION);
		}
		catch (Exception ex)
		{
			Log.d(TAG, ex.getMessage());
		}
	}

	// Upgrading database
	@Override
	public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion)
	{
		// Drop older table if existed
		db.execSQL("DROP TABLE IF EXISTS GpsCurrentLocation");

		// Create tables again
		onCreate(db);
	}

    public <T> long addTableRow(T valueObject, String table)
    {
        return addTableRowWithExclusive(valueObject, table, null);
    }

    public <T> long addTableRowWithExclusive(T valueObject, String table, List<String> exclusiveField)
    {
        if(valueObject == null || table == null || table.length() == 0)
            return -1;

		long rs = -1;
		//--
		synchronized (synchronizedLock)
		{
			Log.i(TAG, String.format("[addTableRowWithExclusive] ------addTableRow: %s -------", table));
			Log.i(TAG, "[addTableRowWithExclusive] " + valueObject.getClass().getName());
			//---
			ContentValues values = new ContentValues();
			for(Field field: valueObject.getClass().getFields())
			{
				try
				{
					if(exclusiveField != null && exclusiveField.contains(field.getName()))
						continue;
					//---
					Type type = field.getGenericType();
					if(type instanceof ParameterizedType)
						continue; //not process yet
					//--
					type = field.getType();

					if(type.equals(String.class))
						values.put(field.getName(), field.get(valueObject).toString());
					else if(type.equals(Long.TYPE))
						values.put(field.getName(), field.getLong(valueObject));
					else if(type.equals(Date.class))
						values.put(field.getName(), Utilities.DateToString((Date) field.get(valueObject)));
					else if(type.equals(Integer.TYPE))
						values.put(field.getName(), field.getInt(valueObject));
					else if(type.equals(Double.TYPE))
						values.put(field.getName(), field.getDouble(valueObject));
				}
				catch (IllegalAccessException e)
				{
					e.printStackTrace();
					Log.e(TAG, "[addTableRowWithExclusive] IllegalAccessException:" + e.toString());
				}
			}

			for(String key: values.keySet())
			{
				Log.i(TAG, "[" + key + "] = " + values.get(key));
			}
			//---
			try
			{
				SQLiteDatabase db = this.getWritableDatabase();
				// Inserting Row
				rs = db.insert(table, null, values);
				db.close(); // Closing database connection
			}
			catch (Exception ex)
			{
				Log.e(TAG, "[addTableRowWithExclusive] EXCEPTION: " + ex.toString());
			}
		}//end of synchronized

        return rs;
    }

    public long addTableRowByContentValues(ContentValues values, String table)
    {
        if(values == null || values.size() == 0 || table == null || table.length() == 0)
            return -1;

        Log.i(TAG, String.format("[addTableRowByContentValues] ------addTableRow: %s -------", table));
        //---
		long rs = -1;

		synchronized (synchronizedLock)
		{
			for(String key: values.keySet())
			{
				Log.i(TAG, "[" + key + "] = " + values.get(key));
			}
			//---
			try
			{
				SQLiteDatabase db = this.getWritableDatabase();
				// Inserting Row
				rs = db.insert(table, null, values);
				db.close(); // Closing database connection
			}
			catch (Exception ex)
			{
				Log.e(TAG, "[addTableRowByContentValues] EXCEPTION: " + ex.toString());
			}
		}//end of synchronized

        return rs;
    }

    public <T> int updateTableRow(T valueObject, String table, String keyField, Object keyValue)
    {
        if(valueObject == null
                || table == null || table.length() == 0
                || keyField == null || keyField.length() == 0
                || keyValue == null)
        {
            return -1;
        }
		int rs = -1;
		synchronized (synchronizedLock)
		{
			Log.i(TAG, String.format("[updateTableRow] ------updateTableRow: %s -------", table));
			Log.i(TAG, "[updateTableRow] " + valueObject.getClass().getName());
			//---
			ContentValues values = new ContentValues();

			for(Field field: valueObject.getClass().getFields())
			{
				try
				{
					Type type = field.getGenericType();
					if(type instanceof ParameterizedType)
						continue; //not process yet
					//--
					type = field.getType();

					if(field.getName().equals(keyField))
						continue;
					//--
					if(type.equals(String.class))
						values.put(field.getName(), field.get(valueObject).toString());
					else if(type.equals(Long.TYPE))
						values.put(field.getName(), field.getLong(valueObject));
					else if(type.equals(Date.class))
						values.put(field.getName(), Utilities.DateToString((Date)field.get(valueObject)));
					else if(type.equals(Integer.TYPE))
						values.put(field.getName(), field.getInt(valueObject));
					else if(type.equals(Double.TYPE))
						values.put(field.getName(), field.getDouble(valueObject));
				}
				catch (IllegalAccessException e)
				{
					e.printStackTrace();
					Log.e(TAG, "[updateTableRow] IllegalAccessException: " + e.toString());
				}
			}

			Log.i(TAG, "Key [" + keyField + "] = " + keyValue);
			for(String key: values.keySet())
			{
				Log.i(TAG, "[" + key + "] = " + values.get(key));
			}
			try
			{
				SQLiteDatabase db = this.getWritableDatabase();
				// updating row
				rs = db.update(table, values, keyField + " = ?",
						new String[] { String.valueOf(keyValue) });

				db.close();
			}
			catch (Exception ex)
			{
				Log.e(TAG, "[updateTableRow] EXCEPTION: " + ex.toString());
			}
		}//end of synchronized

        return rs;
    }

    public int updateTableRow(ContentValues valueObject, String table, String keyField, Object keyValue)
    {
        if(valueObject == null
                || table == null || table.length() == 0
                || keyField == null || keyField.length() == 0
                || keyValue == null)
        {
            return -1;
        }

		int rs = -1;

		synchronized (synchronizedLock)
		{
			Log.i(TAG, String.format("[updateTableRow] ------updateTableRow: %s -------", table));
			//---
			Log.i(TAG, "Key [" + keyField + "] = " + keyValue);
			for(String key: valueObject.keySet())
			{
				Log.i(TAG, "[" + key + "] = " + valueObject.get(key));
			}

			try
			{
				SQLiteDatabase db = this.getWritableDatabase();
				// updating row
				rs = db.update(table, valueObject, keyField + " = ?",
						new String[] { String.valueOf(keyValue) });

				db.close();
			}
			catch (Exception ex)
			{
				Log.e(TAG, "[updateTableRow] EXCEPTION: " + ex.toString());
			}
		}//end of synchronized

        return rs;
    }

    public boolean checkDataRowExists(String table, String keyField, Object value)
    {
        if(value == null)
            throw new NullPointerException("Value cannot null");
		int count = 0;
		//--
		synchronized (synchronizedLock)
		{
			String sql = String.format("select * from %s where %s=?", table, keyField);
			try
			{
				SQLiteDatabase db = this.getReadableDatabase();
				Cursor cursor = db.rawQuery(sql, new String[]{value.toString()});

				if (cursor == null)
					return false;

				count = cursor.getCount();

				cursor.close();
				db.close();
			}
			catch (Exception ex)
			{
				Log.e(TAG, "[checkDataRowExists] EXCEPTION: " + ex);
			}
		}//end of synchronized

        return (count > 0);
    }

    public synchronized long addOrUpdateCurrentLocation(GpsCurrentLocationValue xx)
    {
        boolean x = checkDataRowExists("GpsCurrentLocation", "journalisn", xx.JournalISN);
        long rs;
        if (x)
            rs = updateTableRow(xx, "GpsCurrentLocation", "journalisn", xx.JournalISN);
        else
            rs = addTableRow(xx, "GpsCurrentLocation");

        return rs;
    }

    public synchronized GpsCurrentLocationValue getCurrentLocation(int journalISN)
    {
        String sql = String.format("select * from GpsCurrentLocation where journalisn = ?");
        List<GpsCurrentLocationValue> locations = getTableRows(
                sql,
                new String[]{String.valueOf(journalISN)},
                GpsCurrentLocationValue.class
        );
        if(locations != null && locations.size() > 0)
            return locations.get(0);
        return null;
    }

    public <T> List<T> getTableRows(String query, String[] paramValues, Class<T> cls)
    {
		List<T> result = null;
		synchronized (synchronizedLock)
		{
			try
			{
				Log.d(TAG, "[getTableRows] " + query);
				SQLiteDatabase db = this.getReadableDatabase();
				Cursor cursor = db.rawQuery(query, paramValues);

				if (cursor == null)
					return null;

				if (cursor.moveToFirst())
				{
					result = new ArrayList<T>();
					do
					{
						try
						{
							T xx = cls.newInstance();
							for(Field field : xx.getClass().getFields())
							{
								try
								{
									if(cursor.getColumnIndex(field.getName().toLowerCase()) == -1) //not found
										continue;
									//--
									Type type = field.getGenericType();
									if(type instanceof ParameterizedType)
										continue; //not process yet
									//--
									type = field.getType();

									if(cursor.isNull(cursor.getColumnIndex(field.getName().toLowerCase())))
										continue;

									if(type.equals(String.class))
										field.set(xx, cursor.getString(cursor.getColumnIndex(field.getName().toLowerCase())));
									else if(type.equals(Long.TYPE))
										field.set(xx, cursor.getLong(cursor.getColumnIndex(field.getName().toLowerCase())));
									else if(type.equals(Date.class))
									{
										field.set(xx, Utilities.StringToDate(
												cursor.getString(
														cursor.getColumnIndex(field.getName().toLowerCase()))
										));
									}
									else if(type.equals(Integer.TYPE))
										field.set(xx, cursor.getInt(cursor.getColumnIndex(field.getName().toLowerCase())));
									else if(type.equals(Double.TYPE))
										field.set(xx, cursor.getDouble(cursor.getColumnIndex(field.getName().toLowerCase())));
								}
								catch (Exception ex)
								{
									Log.e(TAG, String.format("[getTableRows] field: %s. %s",
											field.getName(), ex.getMessage()));
								}
							}
							result.add(xx);
						}
						catch (Exception ex)
						{
							Log.e(TAG, "[getTableRows] " + ex.getMessage());
						}
					}while (cursor.moveToNext());
				}
				else
				{
					Log.d(TAG, "[getTableRows] No data found");
				}

				cursor.close();
				db.close();
			}
			catch (Exception ex)
			{
				Log.e(TAG, "[getTableRows] EXCEPTION: " + ex.toString());
			}
		}//end of synchronized

        return result;
    }
}
